#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "gui/GraphicsSquare.h"
#include <QThreadPool>
#include <QCoreApplication>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    solver = new AStarSolver();
    solver->readMap("map.csv");
    QGraphicsView *view = ui->graphicsView;
    view->setScene(new QGraphicsScene());
    view->setRenderHints(QPainter::Antialiasing |
                         QPainter::SmoothPixmapTransform);

    src = Location{0, 0};
    dst = Location{0, 0};
    setSrcDstItem();
    show();

    resetSquare();

    updateViewSize();
    updateSquare();

    connect(solver, SIGNAL(step()), this, SLOT(stepped()));
    connect(ui->btn_step, SIGNAL(clicked()), this, SLOT(goStep()));
    connect(ui->btn_start, SIGNAL(clicked()), this, SLOT(start()));
    connect(ui->btn_stop, SIGNAL(clicked()), this, SLOT(stop()));
    connect(ui->mode_auto, SIGNAL(clicked()), this, SLOT(switchMode()));
    connect(ui->mode_manual, SIGNAL(clicked()), this, SLOT(switchMode()));
    connect(ui->mode_manhattan, SIGNAL(clicked()), this, SLOT(switchMode()));
    connect(ui->mode_euclidian, SIGNAL(clicked()), this, SLOT(switchMode()));
    connect(view->scene(), SIGNAL(changed(QList<QRectF>)), this,
            SLOT(dragTask(QList<QRectF>)));

    QList<QSpinBox *> spins = findChildren<QSpinBox *>();
    foreach (auto spin, spins)
    {
        connect(spin, SIGNAL(valueChanged(int)), this, SLOT(updateTask(int)));
    }
    switchMode();

    updateTask(0);
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::updateViewSize()
{
    int N = std::max(solver->width(), solver->height());
    QGraphicsView *view = ui->graphicsView;
    view->scene()->setSceneRect(0, 0, view->width() * 0.95,
                                view->height() * 0.95);
    GraphicsSquare::setSquareWidth(
        float(std::min(view->width(), view->height()) / (N + 1)));

    srcItem->setScale(GraphicsSquare::SQUARE_WIDTH / 200);
    dstItem->setScale(GraphicsSquare::SQUARE_WIDTH / 230);
}

void MainWindow::resetSquare()
{
    QGraphicsScene *scene = ui->graphicsView->scene();
    scene->clear();
    for (int i = 0; i < solver->width(); ++i)
    {
        for (int j = 0; j < solver->height(); ++j)
        {
            int sq = solver->mapAt(i, j);
            GraphicsSquare *square = new GraphicsSquare();
            square->sqType = sq;
            square->x = i;
            square->y = j;
            scene->addItem(square);
        }
    }

    setSrcDstItem();

    scene->addItem(srcItem);
    scene->addItem(dstItem);

    updateTask(0);
}

void MainWindow::updateSquare()
{
    auto items = ui->graphicsView->scene()->items();
    foreach (auto item, items)
    {
        GraphicsSquare *sq = qgraphicsitem_cast<GraphicsSquare *>(item);
        if (sq != 0)
        {
            Location loc = sq->loc();
            if (loc == src)
                sq->setState(GraphicsSquare::SOURCE);
            else
                sq->setState(GraphicsSquare::SOURCE, false);
            if (loc == dst)
                sq->setState(GraphicsSquare::DESTINATION);
            else
                sq->setState(GraphicsSquare::DESTINATION, false);
            if (solver->came_from.contains(loc))
                sq->setState(GraphicsSquare::TRACE);
            else
                sq->setState(GraphicsSquare::TRACE, false);
            if (solver->cost_total.contains(loc))
            {
                sq->setState(GraphicsSquare::CALCULATED);
                sq->cost = solver->cost_total[loc];
            }
            else
                sq->setState(GraphicsSquare::CALCULATED, false);
            if (solver->path.contains(loc))
            {
                sq->setState(GraphicsSquare::PATH);
            }
            else
                sq->setState(GraphicsSquare::PATH, false);
            if (solver->current_best == loc)
                sq->setState(GraphicsSquare::BEST);
            else
                sq->setState(GraphicsSquare::BEST, false);

            sq->setState(GraphicsSquare::FRONTIER, false);
            foreach (auto f, solver->frontier)
            {
                auto fir = f.second;
                if (loc == fir) sq->setState(GraphicsSquare::FRONTIER);
            }
        }
    }
    foreach (auto loc, solver->came_from)
    {
        auto sq = findSquare(loc.x, loc.y);
        sq->setState(GraphicsSquare::TRACE);
    }
    ui->graphicsView->scene()->update();
}

GraphicsSquare *MainWindow::findSquare(int x, int y)
{
    auto items = ui->graphicsView->scene()->items();
    foreach (auto item, items)
    {
        GraphicsSquare *sq = qgraphicsitem_cast<GraphicsSquare *>(item);
        if (sq != 0 && sq->x == x && sq->y == y) return sq;
    }
    return nullptr;
}

void MainWindow::setSrcDstItem()
{
    srcItem = new QGraphicsPixmapItem(
        QPixmap(QCoreApplication::applicationDirPath() + "/src.png"));
    srcItem->setFlags(QGraphicsItem::ItemIsSelectable |
                      QGraphicsItem::ItemIsMovable);
    srcItem->setAcceptHoverEvents(true);
    srcItem->setTransformationMode(Qt::SmoothTransformation);
    srcItem->setZValue(2);
    dstItem = new QGraphicsPixmapItem(
        QPixmap(QCoreApplication::applicationDirPath() + "/dst.png"));
    dstItem->setFlags(QGraphicsItem::ItemIsSelectable |
                      QGraphicsItem::ItemIsMovable);
    dstItem->setAcceptHoverEvents(true);
    dstItem->setZValue(1);
    dstItem->setTransformationMode(Qt::SmoothTransformation);
    if(findSquare(src.x,src.y)){
        srcItem->setPos(findSquare(src.x,src.y)->pos());
        dstItem->setPos(findSquare(dst.x,dst.y)->pos());
    }
}

void MainWindow::resizeEvent(QResizeEvent *event) { updateViewSize(); }

void MainWindow::closeEvent(QCloseEvent *event) { stop(); }

void MainWindow::stepped() { updateSquare(); }

void MainWindow::goStep()
{
    if (solver->state == AStarSolver::WAITING)
        solver->state = AStarSolver::EXECUTE;
    else if (solver->state == AStarSolver::FREE)
        start();
    ui->graphicsView->scene()->update();
}

void MainWindow::start() {
    if(solver->state!=AStarSolver::FREE)
    {stop(); return;}
    solver->search(src, dst);
    updateSquare();

    if(solver->path.empty()) ui->label->setText("可恶，居然没有找到！");
    else ui->label->setText("搜索到路径，标为黄色！");
}

void MainWindow::stop() { solver->stop(); updateSquare();}

void MainWindow::updateTask(int)
{
    src = Location{ui->spin_startX->value(), ui->spin_startY->value()};
    dst = Location{ui->spin_endX->value(), ui->spin_endY->value()};

    if (!findSquare(src.x, src.y))
    {
        srcItem->setPos(findSquare(src.x, src.y)->pos());
        dstItem->setPos(findSquare(dst.x, dst.y)->pos());
    }
    updateSquare();
}

void MainWindow::switchMode()
{
    if (ui->mode_auto->isChecked())
    {
        solver->mode = AStarSolver::AUTO;
        ui->btn_step->setEnabled(false);
    }
    else
    {
        solver->mode = AStarSolver::MANUAL;
        ui->btn_step->setEnabled(true);
    }
    if (ui->mode_manhattan->isChecked())
    {
        solver->setHMethod(AStarSolver::Manhattan);
    }
    else
    {
        solver->setHMethod(AStarSolver::Euclidean);
    }
}

void MainWindow::dragTask(const QList<QRectF> &)
{
    if (!solver || solver->state != AStarSolver::FREE) return;
    if (!srcItem) return;

    float w = GraphicsSquare::SQUARE_WIDTH;
    auto collis = srcItem->collidingItems();
    foreach (auto colli, collis)
    {
        if (colli->contains(colli->mapFromScene(srcItem->pos()+QPointF(w,w)/2)))
        {
            auto item = qgraphicsitem_cast<GraphicsSquare *>(colli);
            if (item)
            {
                src = item->loc();
                ui->spin_startX->setValue(src.x);
                ui->spin_startY->setValue(src.y);
            }
        }
    }
    auto collis2 = dstItem->collidingItems();
    foreach (auto colli, collis2)
    {
        if (colli->contains(colli->mapFromScene(dstItem->pos()+QPointF(w,w)/2)))
        {
            auto item = qgraphicsitem_cast<GraphicsSquare *>(colli);
            if (item)
            {
                dst = item->loc();
                ui->spin_endX->setValue(dst.x);
                ui->spin_endY->setValue(dst.y);
            }
        }
    }
}
